<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>arrow function</title>
    <style type="text/css">
        .article {
            display: inline-block;
            width: 50%;
            margin-bottom: 1em;
            font-family: sans-serif;
            border-radius: 1rem;
            box-shadow: 12px 12px 2px 1px rgba(0, 0, 0, .2)
        }
        .sidebar {
            width: 25%;
            margin-left: 5px;
        }
        /* Good Instance */
        .article, .sidebar {
            display: inline-block;
        }
        .article {
            width: 50%;
            margin-bottom: 1em;
        }
        .sidebar {
            width: 25%;
            margin-left: 5px;
        }
        .card {
            font-family: sans-serif;
            brder-radius: 1rem;
            box-shadow: 12px 12px 2px 1px rgba(0, 0, 0, .2);
        }
    </style>
</head>
<body>

</body>
<script type="text/javascript">


    /**
     * XHR Instance
     * @param method Request Function
     * @param url Request Api
     */
    function XHR (method, url) {
        var Request = new XMLHttpRequest()
        Request.open(method, url)
        Request.setRequestHeader('CONTENT-TYPE', 'applocation/x-www-from-urlencode')
        Request.send()
        Request.onreadystatechange = function () {
            if (Request.readyState === 4 && Request.status === 200) {
                document.getElementById('content').innerHTML = Request.responseText
            } else {
                document.getElementById('content').innerHTML = Request.readyState + " Api return status" + Request.status
            }
        }
    }
    const calculate = {
        array: [1, 2, 3],
        sum: () => {
            console.log(this === window)
            return this.array.reduce((result, item) => result + item)
        }
    }
    console.log(this === window)
    calculate.sum()

    const calculate = {
        array: [1, 2, 3],
        sum () {
            console.log(this === calculate)
            return this.array.reduce((result, item) => result +item)
        }
    }

    const button = document.getElementById('myButton')
    button.addEventListener('click', () => {
        console.log(this === button);
        this.innerHTML = 'Clicked button'
    })

    // 调用构造函数
    const Message = (text) => {
        this.text = text;
    }
    // throwns "TypeError: Message is not a constructor"
    const helloMessage = new Message('Hello World!')

    /**
     * 点击行为
     * 用户行为日志, 继承于日志基类MonitorBaseInfo
     *
     */


    function BehaviorInfo (uploadType, behaviorType, className, placeholder, inputValue, tagName, innerText) {
        setCommonProperty.apply(this)
        this.uploadType = uploadType
        this.behaviorType = behaviorType
        this.className = utils.b64EncodeUnicode(className)
        this.placeholder = utils.b64EncodeUnicode(placeholder)
        this.inputValue = utils.b64EncodeUnicode(inputValue)
        this.tagName = tagName
        this.innerText = utils.b64EncodeUnicode(encodeURIComponent(innerText))
    }

    /**
     *  用户行为记录监控
     *  @param project 项目详情
     */
    function recordBehavior (project) {
        if (project && project.record && project.record == 1) {
            checkUrlChange()
            document.onclick = function (e) {
                var className = ''
                var placeholder = ''
                var inputValue = ''
                var tagName = e.target.tagName
                var innerText = ''
                if (e.target.tagName != 'svg' && e.target.tagName != 'use') {
                    className = e.target.className
                    placeholder = e.target.placeholder || ""
                    inputValue = e.target.value || ""
                    innerText = e.target.innerText.replace(/\s*/g, '')
                    if (innerText.length > 200) innerText = innerText.substring(0, 100) + "... ..." + innerText.substring(innerText.length - 99, innerText.length - 1)
                    innerText = innerText.replace(/\s/g, '')
                }
                var behaviorInfo = new BehaviorInfo(ELE_BEHAVIOR, 'click', className, placeholder, inputValue, tagName, innerText)
                behaviorInfo.handleLogInfo(ELE_BEHAVIOR, behaviorInfo)
            }
        }
    }

    /**
     * Request api. 继承于日志基类MonitorBaseInfo
     *
     */
    function HttpLogInfo (uploadType, url, status, statusText, statusResult, currentTime) {
        setCommonProperty.apply(this)
        this.uploadType = uploadType
        this.httpUrl = utils.b64EncodeUnicode(url)
        this.status = status
        this.statusText = statusText
        this.statusResult = statusResult
        this.happenTime = currentTime
    }

    /**
     *  Request api page controller
     *
     */
    function recordHttplog () {
        // controller ajax staus
        function ajaxEventTrigger (event) {
            var ajaxEvent = new CustomEvent(event, {
                detail: this
            })
            window.dispatchEvent(ajaxEvent)
        }
        var oldXHR = window.XMLHttpRequest()
        function newXHR () {
            var realXHR = new oldXHR()
            realXHR.addEventListener('abort', function () {
                ajaxEventTrigger.call(this, 'ajaxAbort')
            }, false)
            realXHR.addEventListener('error', function () {
                ajaxEventTrigger.call(this, 'ajaxError')
            }, false)
            realXHR.addEventListener('load', function () {
                ajaxEventTrigger.call(this, 'ajaxLoad')
            }, false)
            realXHR.addEventListener('loadStart', function () {
                ajaxEventTrigger.call(this, 'ajaxLoadStart')
            }, false)
            realXHR.addEventListener('progress', function () {
                ajaxEventTrigger.call(this, 'ajaxProgress')
            }, false)
            realXHR.addEventListener('timeout', function () {
                ajaxEventTrigger.call(this, 'ajaxTimeout')
            }, false)
            realXHR.addEventListener('loadend', function () {
                ajaxEventTrigger.call(this, 'ajaxLoadEnd')
            }, false)
            realXHR.addEventListener('readystatechange', function () {
                ajaxEventTrigger.call(this, 'ajaxReadStatechange')
            }, false)
            return realXHR
        }
        window.XMLHttpRequest = newXHR
        window.addEventListener('ajaxLoadStart', function (e) {
            var currentTime = new Date().getTime()
            setTimeout(function () {
                var url = e.detail.responseURL
                var status = e.detail.status
                var statusText = e.detail.statusText
                if (!url || url.indexOf(HTTP_UPLOAD_LOG_API) != -1) return
                var httpLogInfo.handleLogInfo(HTTP_LOG, httpLogInfo)
            }, 2000)
        })
        window.addEventListener('ajaxLoadEnd', function (e) {
            var currentTime = new Date().getTime()
            var url = e.detail.responseURL
            var status = e.detail.status
            var statusText = e.detail.statusText
            if (!url || url.indexOf(HTTP_UPLOAD_LOG_API) != -1) return
            var httpLogInfo = new HttpLogInfo(HTTP_LOG, url, status, statusText, "Return Request", currentTime)
            HttpLogInfo.handleLogInfo(HTTP_LOG, httplogInfo)
        })
    }


    // ES6继承
    // 父类
    function supFather (name) {
        this.name = name
        this.colors = ['red', 'blue', 'green']
    }
    supFather.prototype.sayName = function (age) {
        console.log(this.name, 'age')
    }
    // 子类
    function sub (name, age) {
        supFather.call(this, name)
        this.age = age
    }
    // 重写子类的prototype,  修正constructor
    function inheritPrototyep (sonFn, fatherFn) {
        sonFn.prototype = Object.create(fatherFn.prototype)
        sonFn.prototype.constructor = sonFn
    }
    inheritPrototype(sub, supFather)
    sub.prototype.sayAge = function () {
        console.log(this.age, 'foo')
    }
    const instance1 = new sub('OBKoro1', 24)
    const instance2 = new sub('xiaoming', 18)
    instance1.colors.push('black')
    console.log(instance1)
    console.log(instance2)

    // apply
    const obj = {
        age: 24,
        name: 'OBKoro1'
    }
    const obj2 = {
        age: 777
    }
    callObj(obj, handle)
    callObj(obj2, handle)
    // 根据某些条件来决定要传递参数的数量、以及顺序
    function callObj (thisAge, fn) {
        let params = []
        if (thisAge.name) {
            params.push(thisAge.name)
        }
        if (thisAge.age) {
            params.push(thisAge.age)
        }
    }
    function hadnle(...params) {
        console.log('params', params)
    }
    // bind的应用场景
    for (var i = 1; i <= 5; i ++) {
        setTimeout(function test () {
            console.log(i)
        }, i * 1000)
    }
    // console.log 6 6 6 6 6 6

    // 使用闭包
    for (var i = 1; i <= 5; i ++) {
        (function (i) {
            setTimeout(function () {
                console.log(i)
            }, i * 1000)
        })(i)
    }
    // 使用bind
    for (var i = 1; i <= 5; i++) {
        // 缓存参数
        setTimeout(function (i) {
            console.log('bind', i) // 依次输出：1 2 3 4 5
        }.bind(null, i), i * 1000);
    }

    class Page {
        constructor  (callBack) {
            this.className = 'Page'
            this.MessageCallBack = callBack
            this.MessageCallBack ('Request login page it Message')
        }
    }


    // 分页数据
    const getFib = (number) => {
        if (number <= 2) {
            return 1
        } else {
            return getFib(number - 1)  + getFib(number + 2)
        }
    }
    const getCacheProxy = (fn, cache = new Map()) => {
        return new Proxy (fn, {
            apply(target, context, args) {
                const argsString = args.join('')
                if (cache.has(argsString)) {
                    console.log(${cache.get(argsString)})
                    return cache.get(argsString)
                }
                const result = fn(...args)
                cache.set(argsString, result)
                return result
            }
        })
    }
    const getFibProxy = getCacheProxy(getFib)
    getFibProxy(40)

    // 装饰者模式
    var xiaoming = function () {
        this.run = function () {
            return '...'
        }
        this.eat = function () {
            return '...'
        }
    }
    var decor = function (xiaoming) {
        this.run = function () {
            return xiaoming.run + '...'
        }
        this.eat = function () {
            return xiaoming.eat + '...'
        }
    }
    // 手写call/apply、bind
    Function.prototype.myCall = function (context, ...arr) {
        if (context === null || context ==== undefined) {
            context = window
        } else {
            context = Object(context)
        }
        context.testFn = this
        let result = context.testFn(...arr)
        delete context.testFn
        return result
    }

    context = context ? Object(context) : window
    context = context || window

    //
    function handle (...params) {
        this.test = 'handle'
        console.log('params', this, ...params)
        handle.elseCall('')
        handle.elseCall(0)
        handle.seleCall(false)
    }

    // 手写一个apply
    Function.prototype.myApply = function (context) {
        if (context === null || context === undefined) {
            context = window
        } else {
            context = Object(context)
        }
        function isArrayLike (o) {
            if (o && typeof o === 'object' && isFinite(o.length) && o.length >= 0 && o.length === Math.floor(o.length) && o.length < 4294967296)
                return true
            else
                return false
        }
        context.testFn = this
        const args = arguments[1]
        let result
        if (args) {
            if (!Array.isArray(args) && !isArrayLike(args)) {
                throw new TypeError('...')
            } else {
                args = Array.from(args)
                result = context.testFn(...args)
            }
        } else {
            result = contex.testFn()
        }
        delete context.testFn
        return result
    }


    // copy 函数
    Function.prototype.myBind = function (objThis, ...params) {
        const thisFn = this
        let fToBind = function (...secondParams) {
            const isNew = this instanceof fToBind
            const context = isNew ? this : Object(objThis)
            return thisFn.apply(context, ...params, ...secondParams)
        }
        fToBind.prototype = Object.create(thisFn.prototype)
        return fToBind
    }
    // 实现一个call函数
    Function.prototype.mycall = function (context) {
        if (typeof this !== 'function') {
            throw new TypeError('not function')
        }
        context = context || window
        context.fn = this
        let arg = [...arguments].slice(1)
        let result = context.fn(...arg)
        delete context.fn
        return result
    }
    // 实现一个apply函数
    Function.prototype.myapply = function (context) {
        if (typeof this !== 'function') {
            throw new TypeError('not function')
        }
        context = context || window
        context.fn = this
        let result
        if (arguments[1]) {
            result = context.fn(...arguments[1])
        } else {
            result = context.fn()
        }
        delete context.fn
        return result
        // 实现一个bind函数
        Function.prototype.mybind = function (context) {
            if (typeof this !== 'function') {
                throw new TypeError('Error')
            }
            let _this = this
            let arg = [...arguments].slice(1)
            return function F () {
                if ( this instanceof F) {
                    return new _this(...arg, ...arguments)
                } else {
                    return _this.apply(context, arg.concat(...arguments))
                }
            }
        }
    }

    // instanceof原理
    function instanceOf (left, right) {
        let leftValue = left.__proto__
        let rightValue = right.prototype
        while (true) {
            if (leftValue === null) {
                return false
            }
            if (leftValue === rightValue) {
                return true
            }
            leftValue = leftValue.__proto__
        }
    }

    // Object.create的基本实现原理
    function create (obj) {
        function F () {}
        F.prototype = obj
        return new F()
    }

    // new 本质
    function myNew (fun) {
        return function () {
            let obj = {
                __proto__ : fun.prototype
            }
            fun.call(obj, ...arguments)
            return obj
        }
    }
    function person (name, age) {
        this.name = name
        this.age = age
    }
    let obj = myNew(person)('chen', 18)

    // 实现一个基本的Promise
    class Promise {
        constructor (fn) {
            this.state = 'pending'
            this.value = undefined
            this.reason = undefined
            let resolve = value => {
                if (this.state === 'pending') {
                    this.state = 'fullfilled'
                    this.value = value
                }
            }
            let reject = value => {
                if (this.state === 'pending') {
                    this.state = 'rejected'
                    this.reason = value
                }
            }
            try {
                fn (resolve, reject) {

                }
            } catch (e) {
                reject(e)
            }
        }
        then(onFulfilled, onRejected) {
            switch (this.state) {
                case 'fulfilled':
                    onFulfilled()
                    break
                case 'rejected':
                    onRejected()
                    break
                default:
            }
        }
    }

    // 实现一个浅拷贝
    let copy1 = {...{x:1}}
    let copy2 = Object.assign({}, {x:1})

    // 实现一个深拷贝
    let obj = {a:1, b: {x: 3}}
    JSON.parse(JSON.stringify(obj))

    function deepClone (obj) {
        let copy = obj instanceof Array ? [] : {}
        for (let i in obj) {
            if (obj.hasOwnProperty(i)) {
                copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
            }
        }
        return copy
    }

    // 使用setTimeout 模拟 setInterval
    setTimeout (function () {
        setTimeout(arguments.callee, 500)
    }, 500)

    // Javascript 实现一个继承方法
    function Child () {
        Parent.call(this)
    }
    (function () {
        let Super = function () {}
        Super.prototype = Parent.prototype
        Child.prototype = new Super()
    })

    // 实现一个基本的Events Bus
    class EventEmitter {
        constructor () {
            this.evetns = this.events || new Map()
        }

        addListener (type, fn) {
            if (!this.evetns.get(type)) {
                this.evetns.set(type, fn)
            }
        }

        emit (type) {
            let handle = this.evetns.get(type)
            handle.apply(this, [...arguments].slice(1))
        }
    }
    let emiter = new EventEmitter()
    emitter.addListener('ages', age => {
        console.log(age)
    })
    emitter.emit('ages', 18)

    // 实现一个双向的数据绑定
    let obj = {}
    let input = document.getElementById('input')
    let span = document.getElementById('span')
    Object.defineProperty(obj, 'text', {
        configurable: true,
        enumerable: true,
        get () {
            console.log('get new Message')
        }
    })

    // 循环实现 array map 方法
    const selfMap = function (fn, context) {
        let Arr = Array.prototype.slice.call(this)
        let mappedArr = Array(arr.length - 1)
        for (let i = 0; i < arr.length; i ++) {
            if (!arr.hasOwnProperty(i)) continue;
            mappedArr[i] = fn.call(context, arr[i], i, this)
        }
        return mappedArr
    }
    // 使用方法
    Array.prototype.selMap = selMap;
    [1, 2, 3].selMap(number => number * 2)


    // 使用 reduce 实现数组map方法
    const selfMap2 = function (fn, context) {
        let arr = Array.prototype.slice.call(this)
        return arr.reduce((pre, cur, index) => {
            return [...pre, fn.call(context, cur, index, this)]
        }, [])
    }

    // 循环实现数组filter方法
    const selfFilter = function (fn, context) {
        let arr = Array.prototype.slice.call(this)
        let filetereAdd = []
        for (let i = 0; i < arr.length; i ++) {
            if (!arr.hasOwnProperty(i)) continue;
        }
        return filteredArr;
    }

    // 循环实现 array some 方法
    const selfSome = function (fn, context) {
        let arr = Array.prototype.slice.call(this)
        if (!arr.length) return false
        for (let i = 0; i < arr.length; i ++) {
            if (!arr.hasOwnProperty(i)) continue
            let res = fn.call(context, arr[i], i, this)
            if (res) return true
        }
        return false
    }

    // 循环实现数组的reduce方法
    Array.prototype.selfReduce = function (fn, initialValue) {
        let arr = Array.prototype.slice.call(this)
        let res
        let startIndex
        if (initialValue === undefined) {
            for (let i = 0; i < arr.length; i ++) {
                if (!arr.hasOwnProperty(i)) continue
                startIndex = i
                res = arr[i]
                break
            }
        } else {
            res = initialValue
        }
        for (let i = ++ startIndex || 0; i < arr.length; i ++) {
            if (!arr.hasOwnProperty(i)) continue
            res = fn.call(null, res, arr[i], i, this)
        }
        return res
    }

    // 使用reduce实现array flat方法
    const selfFlat = function (depth = 1) {
        let arr = Array.prototype.slice.call(this)
        if (depth === 0) return arr
        return arr.reduce((pre, cur) => {
            if (Array.isArray(cur)) {
                return [...pre, ...selfFlat.call(cur, depth - 1)]
            } else {
                return [...pre, cur]
            }
        }, [])
    }

    // ES6 的 class语法
    function inherit (subType, superType) {
        subType.prototype = Object.create(superType.prototype, {
            construct: {
                enumberable: false,
                configurable: true,
                writable: true,
                value: subType
            }
        })
        Object.setPrototypeOf(subType, superType)
    }

    // 函数柯里化
    function curry (fn) {
        if (fn. length <= 1) return fn
        const generator = (...args) => {
            if (fn.length === args.length) {
                return fn(...args)
            } else {
                return (...args2) => {
                    return generator(...args, ...args2)
                }
            }
        }
        return generator
    }

    const curry3 = (fn, placeholder = '_') => {
        curry3.placeholder = placeholder
        if (fn.length <= 1) return fn
        let argsList = []
        const generator = (...args) => {
            let currentPlaceholderIndex = -1
            args.forEach (arg => {
                let placeholderIndex = argsList.findIndex(
                    item => item === curry3.placeholder
                )
                if (placeholderIndex < 0) {
                    currentPlaceholderIndex = argsList.push(arg) - 1
                } else if (placeholderIndex !== currentPlaceholderIndex) {
                    argsList[placeholderIndex] = arg
                } else {
                    argsList.push(arg)
                }
            })
            let realArgsList = argsList.filter(arg => arg !== curry3.placeholder) {
                if (realArgsList.length === fn.length) {
                    return fn(...argsList)
                } else if (realArgsList.length > fn.length) {
                    throw new Error('max value not found')
                } else {
                    return generator
                }
            }
            return generator
        }
    }

    // 偏函数
    const partialFunc = (func, ...args) => {
        let palceholderNum = 0
        return (...args2) => {
            args2.forEach(arg => {
                let index = args.findIndex(item => item === '_')
                    if (index < 0) return
                    args[index] = arg
                    placeholderNum ++
            })
            if (placeholderNum < args2.length) {
                args2 = args2.slice(placeholderNum, args2.length)
            }
            return func.apply(this, [...args, ...args2])
        }
    }

    // 斐波那契数列及其优化
    let fibonacci = function (n) {
        if (n < 1) throw new Error('params Error')
        if (n === 1 || n === 2) return 1
        return fibonacci(n - 1) + fibonacci(n - 2)
    }
    const memory = function (fn) {
        let obj = {}
        return function (n) {
            if (obj[n] === undefined) obj[n] = fn(n)
            return obj[n]
        }
    }
    fibonacci = memory(fibonacci)

    // 实现bind方法
    const isComplexDataType = obj =>
        (
            typeof obj === 'object' || typeof obj === 'funtion'
        ) &&
            obj !== null
    const selfBind = function (bindTarget, ...args1) {
        if (typeof this !== 'function') {
            throw new TypeError('Bind must be called on a function')
        }
        let func = this
        let boundFunc = function (...args2) {
            let args = [...args1, ...args2]
            if (new.target) {
                let res = func.apply(this, args)
                if (isComplexDataType(res)) return res
                return this
            } else {
                func.apply(bindTarget, args)
            }
        }
        this.prototype && (boundFunc.prototype = Object.create(this.prototype))
        let desc = Object.getOwnPropertyDescriptors(func)
        Object.defineProperties(boundFunc, {
            length: desc.length,
            name: Object.assign(desc.name, {
                value: `bound${desc.name.value}`
            })
        })
        return boundFunc
    }

    // 实现函数call方法
    const selfCall = function (context, ...args) {
        let func = this
        context || (context = window)
        if (typeof func !== 'function') throw new TypeError('This is not found.')
        let caller = Symbol('caller')
        context[caller] = func
        let res = context[caller](...args)
        delete context[caller]
        return res
    }


    /**
     *  定义一个公共的 filter
     *  用于处理一些 通用的页面拦截逻辑
     *  @param resolve
     *  @param rejected
     */

    var filter = function (resolve, reject) {
        let excutors = []

        new Filter1(excutors, promiseQueue)
        new Filter2(excutors, promiseQueue)
        new Filter3(excutors, promiseQueue)
        promiseQueue(excutors).then(() => {
            resolve(true)
        }).catch(cb => {
            reject(cb)
        })
    }

    function promiseQueue (executors, letGo) {
        return new ES6Promise((resolve, reject) => {
            if (!Array.isArray(executors)) {
                executors = Array.from(executors)
            }
            if (executors.length <= 0) {
                return resolve([])
            }
            var res = []
            executors = executors.map((x, i) => () => {
                var p = typeof x === 'function' ? new ES6Promise(x) : ES6Promise.resolve(x)
                p.then(response => {
                    res[i] = response
                    if (i === executors.length - 1) {
                        resolve(res)
                    } else {
                        executors[i + 1]()
                    }
                }, reject)
            })
            executors[0]()
            if (letGo) {
                resolve(true)
            }
        })
    }


    // 防抖函数
    const debounce = (fn, delay) => {
        let timer = null;
        return (...args) => {
            clearTimeout(timer)
            timer = setTimeout(() => {
                fn.apply(this, args)
            }, delay)
        }
    }

    // 实现节流函数(throttle)
    const throttle = (fn, delay = 500) => {
        let flag = true;
        return (...args) => {
            if (!flag) return
            flag = false;
            setTimeout(() => {
                fn.apply(this, args)
                flag = true
            }, delay)
        }
    }

    // 深克隆 (deepclone)
    const clone = parent => {
        const isType = (obj, type) => {
            if (typeof obj !== 'object') return false
            const typeString = Object.prototype.toString.call(obj)
            let flag
            switch (type) {
                case "Array":
                    flag = typeString === '[object Array]'
                    break
                case "Date":
                    flag = typeString === '[object Date]'
                    break
                case "RegExp":
                    flag = typeString === '[object RegExp]'
                    break
                default:
                    flag = false
            }
            return flag
        }
        const getRegExp = re => {
            var flags = ''
            if (re.global) flags += 'g'
            if (re.ignoreCase) flags += 'i'
            if (re.multiline) flags += 'm'
            return flags
        }
        const parents = []
        const children = []
        const _clone = parent => {
            if (parent === null) return null
            if (typeof parent !== 'object') return parent
            let child, proto
            if (isType(parent, 'Array')) {
                child = []
            } else if (isType(parent, 'RegExp')) {
                child = new RegExp(parent.source, getRegExp(parent))
                if (parent.lastIndex) child.lastIndex = parent.lastIndex
            } else if (isType(parent, 'Date')) {
                child = new Date(parent.getTime())
            } else {
                proto = Object.getPrototypeOf(parent)
                child = Object.create(proto)
            }
            const index = parents.indexOf(parent)
            if (index != -1) {
                return children[index]
            }
            parents.push(parent)
            children.push(child)
            for (let i in parent) {
                child[i] = _clone(parent[i])
            }
            return child
        }
        return _clone(parent)
    }

    class EventEmitter {

    }

    var calculator = ( () => {
        var initVal = 1
        function addCalculator (val) {
            initVal += val
        }
        return {
            add: (addVal) => {
                addCalculator(addVal)
            },
            result: () => {
                return initVal
            }
        }
    })()


    console.log(calculator.result())
    calculator.add(20)
    console.log(calculator.result())

    let list = [
        {id: '1', name: 'jsliang'},
        {id: '2', name: 'xxx'}
    ];
    function insertData (rowId) {
        list = [
            ...list.slice(0, list.findIndex(item => item.id === rowId) + 1),
            {
                id: '3',
                name: ''
            },
            ...list.slice(list.findIndex(item => item.id === rowId) + 1)
        ]
    }
    insertData('1')
    console.log(list)


    function isBigEnough (element) {
        return element >= 10
    }
    var filtered = [12, 5, 8, 130, 44].filter(isBigEnough)
    const number = [12, 5, 8, 130, 44]
    const filterNumber = number.filter(item => item >= 10)

    const filterNumber = number.filter((item, index, array) => {
        console.log(array)
        return item >= 10 && index > 3
    })

    let list = [
        {id: '1', name: 'jsliang'},
        {id: '2', name: 'xxx'},
        {id: '3', name: 'JavaScriptLiang'}
    ];
    function changeData (id, newObj) {
        list = [...list.filter(item => item.id !== id), newObj]
    }
    changeData('2', {
        id: '4',
        name: 'xxx'
    })
    [
            {id: '1', name: 'jsliang'},
            {id: '3', name: 'JavaScript'},
            {id: '4', name: 'xxx'}
    ]
    let list = [
        {id: '1', name: 'jsliang'},
        {id: '2', name: 'xxx'},
        {id: '3', name: 'JavaScriptLiang'}
    ]
    const ids = list.map(item => item.id)
    const id1 = list[list.findIndex(item => item.name === 'JavaScriptLiang')].id
    const id2 = list.find(item => item.name === 'JavaScriptLiang').id


    const items = ['item1', 'item2', 'item3']
    const copy = []
    for (let i = 0; i < items.length; i ++) {
        copy.push(items[i])
    }
    items.forEach(function (item) {
        copy.push(item)
    })

    let arr = [1, 2, 3, 4, 5]
    arr = new Array(arr.length).fill(0)

    [1, 2, 3, 4].reduce((prev, next) => {
        return prev + next
    })
    ['前端', 'pang', 'liagn'].reduce((prev, next, index) => {
        return (index = 0 ? '-js' : '') + prev + 'js' + next
    })


    var inventory = [
        {name: 'apples', quantity: 2},
        {name: 'banans', quantity: 0},
        {name: 'cherries', quantity: 5}
    ];
    function findCherries (fruit) {
        return fruit.name === 'cherries'
    }
    inventory.find(findCherries)

    var array1  = [5, 12, 8, 130, 44]
    function isLargeNumber (element) {
        return element > 13
    }
    array1.findIndex(isLargeNumber)


    var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg']
    function loadImg (url) {
        return new Promise((resolve, reject) => {
            const img = new Image()
            img.onload = function () {
                console.log('one image loading over')
                resolve()
            }
            img.onerror = reject
            img.src = url
        })
    }
    function limitLoad (urls, handler, limit) {
        const sequence = [].concat(urls)
        let promises = []
        promises = sequence.splice(0, limit).map((url, index) => {
            return hadnler(url).then(() => {
                return index
            })
        })
        return sequence.reduce((last, url, currentIndex) => {
            return last.then(() => {
                return Promise.race(promises)
            }).catch(err => {
                console.error(err)
            }).then((res) => {
                promises[res] = handler(sequence[currentIndex]).then(() => { return res })
            })
        }, Promise.resolve()).then(() => {
            return Promise.all(promises)
        })
    }
    limitLoad(urls, loadImg, 3).then(() => {
        console.log('all images load over')
    }).catch(err => {
        console.error(err)
    })


    const genUid = () => {
        var length = 20
        var soupLength = genUid.soup_.length
        var id = []
        for (var i = 0; i < length; i ++) {
            id[i] = genUid.soup_.charAt(Math.random() * soupLength)
        }
        return id.join('')
    }
    genUid.soup_ = ''
    genUid()

    // 遍历类数组对象
    const elements = document.querySelectorAll(selector)
    [].prototype.forEach.call(elements, (el, idx, list) => {
        console.log(el)
    })

    // 优化多层判断条件
    const getScore = score => {
        const scoreData = new Array(101).fill(0).map((data, idx) => ([idx, () => (idx < 60 ? 'OK' : 'NO')]))
        const scoreMap = new Map(scoreData)
        return (scoreMap.get(score) ? scoreMap.get(score) : 'don\'t know number')
    }
    getScore(30)

    // 时间格式化
    const dateFormatter = (formatter, date) => {
        date = (date ? new Date(date) : new Date)
        const Y = date.getFullYear() + '',
            M = date.getMonth() + 1,
            D = date.getDate(),
            H = date.getHours(),
            m = date.getMinutes(),
            s = date.getSeconds()
        return formatter.replace(/YYYY|yyyy/g, Y)
            .replace(/YY|yy/g, Y.substr(2, 2))
            .replace(/MM|g, (M < 10 ? '0' : '') + M)
                .replace(/DD/g, (D < 10 ? '0' : '')) + D)
            .replace(/HH/hh/g, (H < 10 ? '0' : '') + H)
        .replace(/mm/g, (m < 10 ? '0' : '') + m)
            .replace(/ss/g, (s < 10 ? '0' : '') + s)
    }
    dateFormatter('YYYY-MM-DD HH:mm', '1995/02/15 13:55')


    var date = new Date()
    date.setMonth(date.getMonth() + 1)
    let lastDay = date.setDate(0)
    console.log('last day: ' + new Date(lastDay).toLocaleString())


    var date = new Date(2019, 0, 31)
    date.setMonth(date.getMonth() + 1)
    let lastDay = date.setDate(0)
    console.log('last day: ' + new Date(lastDay).toLocaleString())

    var date = new Date(2019, 0, 31)
    date.setDate(28)
    date.setMonth(date.getMonth() + 1)
    let lastDay = date.setDate(0)
    console.log('last day: ' + new Date(lastDay).toLocaleString())



    const factorialArguments = function (num) {
        if (num <= 1) {
            return 1
        } else {
            return num * arguments.callee(num - 1)
        }
    }
    console.log('5 x', factorialArguments(5))
    const factorial = function f (num) {
        if (num <= 1) {
            return 1
        } else {
            return num * f(num - 1)
        }
    }
    console.log('5 x', factorial(5))


    const sum = function sum (a, b) {
        return a + b
    }
    const sumByCall = function (a, b) {
        return sum.call(this, a, b)
    }
    const sumByApply = function (a, b) {
        return sum.apply(this, [a, b])
    }
    const sumByApplyArguments = function (a, b) {
        return sum.apply(this, arguments)
    }

    console.log('sum :', sum(2, 7))
    console.log('sumByCall :', sumByCall(2, 7))
    console.log('sumByApply :', sumByApply(2, 7))
    console.log('sumByApplyArguments :', sumByApplyArguments(2, 7))



    const transmitByValue = function (str) {
        str += ', wo shi pr'
        return str
    }

    const str = 'Hello'
    console.log('str before', str)
    console.log('str by transmitByValue', transmitByValue(str))
    console.log('str after', str)


    const transmitByAddress = function (str) {
        str.words += '. woshipr'
        return str
    }
    const str = {
        worlds: 'hello'
    }
    console.log('str before :', str.words)
    console.log('str by transmitByAddress', transmitByAddress(str))
    console.log('str after', str.worlds)

    const closure = function () {
        let num = 10
        return function () {
            return ++num
        }
    }
    const closureAlias = closure()
    console.log(closureAlias())
    console.log(closureAlias())
    console.log(closureAlias())
    console.log(closureAlias())
    console.log('------')
    console.log(closure()())
    console.log(closure()())
    console.log(closure()())


    var obj = {
        name:'say',
        say () {
            return function () {
                return this
            }
        }
    }
    console.log(obj.say()())

    var obj = {
        name: 'say',
        say () {
            return function () {
                return this
            }
        }
    }
    console.log(obj.say().call(obj))


    var obj = {
        name: 'say',
        say () {
            var that = this
            return function () {
                return that
            }
        }
    }

    for (var i = 0; i <= 9; i ++) {
        setTimeout(function () {
            console.log(i)
        })
    }

    for (var i = 0; i <= 9; i ++) {
        (function () {
            console.log(i)
        })(i)
    }

    for (let i = 0; i <= 9; i ++) {
        console.log(i)
    }

    const movies = {
        list: [
            { title: 'heat' },
            { title: 'Interstellar' }
        ],
        [Symbol.iterator]() {
            let index = 0
            return {
                next: () => {
                    if (index < this.list.length) {
                        const value = this.list[index ++].title
                        return {value, done: false}
                    }
                    return {done:true}
                }
            }
        }
    }

    const [firstMovieTitle] = movies
    console.log(firstMovieTitle)

    function greet (obj, nameProp) {
        const {[nameProp]: name = 'Unknown'} = obj
        return `Hello, ${name}!`
    }
    greet({name: 'Batman'}, 'name') // => 'Hello, Batman!'
    greet({ }, 'name') // => 'Hello, Unknown'

    function SuperType () {
        this.status = true
    }
    SuperType.prototype.getStatus = function () {
        return this.status
    }
    function SubType () {
        this.subStatus = false
    }
    SubType.prototype.getSubStatus = function () {
        return this.subStatus
    }
    SubType.prototype = new SuperType()
    var foo = new SubType()

    function SuperType () {
        this.name = ['dag', 'cat']
    }
    function SubType () {}
    SubType.prototype = new SuperType()
    var instance1 = new SubType()
    var instance2 = new SubType()
    console.log(instance2.name)

    function Super () {
        this.name = ['Mike', 'David']
    }
    Super.prototype.addname = function (name) {
        this.name.push(name)
    }
    function Sub () {
        Super.call(this)
    }
    var foo = new Sub()
    foo.name // ['Mike', 'David']

    function Super () {
        this.name = ['Mike', 'David']
    }
    Super.prototype.addname = function (name) {
        this.name.push(name)
    }
    function Sub () {
        Super.call(this)
    }
    Sub.prototype = new Super()
    Sub.prototype.constructor = Sub
    Sub.prototype.getName = function () {
        console.log(this.name.join(','))
    }
    var foo = new Sub()

    function extend (obj) {
        function noop () {
            noop.prototype = obj
            return new noop()
        }
        var Animals = {
            name: 'animal',
            type: ['dog', 'cat', 'bird']
        }
        var anotherAnimals = extends(Animals)
        anotherAnimals.type.push('horse')

        var yetAnotherAnimal = extends(Animals)
        yetAnotherAnimal.type.push('whale')
        console.log(Animals.type) // ['dog', 'cat', 'bird', 'horse', 'whale']

        var Animals = {
            name: 'animal',
            type: ['dog', 'cat', 'bird']
        }
        var anotherAnimals = Object.create(Animals)
        anotherAnimals.type.push('horse')
    }


    function cusExtend (obj) {
        var clone = extend(obj)
        clone.foo = function () {
            console.log('foo')
        }
        return clone
    }
    var Animals = {
        name:'animal',
        type: ['dog', 'cat', 'bird']
    }
    var instance = cusExtend(Animals)
    instance.foo()

    function SuperType (name) {
        this.name = name
    }
    function SubType (name, age) {
        SuperType.call(this, name, age)
        this.age = age
    }
    SubType.prototype = Object.create(SuperType.prototype)
    SubType.prototype.constructor = SubType
    var foo = new SubType('Mike', 16)

    function objectCeate (o) {
        var F = function () {}
        var prototype = o.prototype
        F.prototype = prototype
        return new F()
    }

    class SuperType {
        constructor (name) {
            this.name = name
        }
    }
    class SubType extends SuperType {
        constructor (name, age) {
            super(name)
            this.age = age
        }
    }

    foo.__proto__ => SubType.prototype
    SubType.prototype.__proto__ => SupperType.prototype


    SubType.__proto__ => SuperType

    class A {

    }

    class B extends A {

    }

    Object.setPrototypeOf(B, A)
    Object.setPrototypeOf(B.prototype, A.prototype) // B.prototype.__proto__ === A.prototype


    Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
        obj.__proto__ = proto
        return obj
    }

    'use strict'

    function _typeof (obj) {
        if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
            _typeof = function _typeof(obj) {
                return typeof obj
            }
        } else {
            _typeof = function _typeof (obj) {
                return obj &&
                typeof SYmbol === 'function' &&
                obj.constructor === Symbol &&
                obj !== Symbol.prototype
                    ? 'symbol'
                    : typeof obj
            }
        }
        return _typeof(obj)
    }

    function _possibleCOnstructorReturn (self, call) {
        if (call && (_typeof(call) === 'object' || typeof call === 'function')) {
            return call
        }
        return _assertThisInitialized(self)
    }

    function _assertThisInitialized(self) {
        if (self === void 0) {
            throw new ReferenceError(
                'this hasn\'t been initialised - super() hasn\'t been called'
            )
        }
        return self
    }

    function _getPrototypeOf (o) {
        _getPrototypeOf = Object.setPrototypeOf
        ? Object.getPrototypeOf
            : function _getPrototypeOf (o) {
                return o.__proto__ || Object.getPrototypeOf(o)
            }
            return _getPrototypeOf(o)
    }

    function _inherits (subClass, superClass) {
        if (typeof superClass !== 'function' && superClass != null) {
            throw new TypeError('Super expression must either be null or a function')
        }
        subClass.prototype = Object.create(superClass && superClass.prototype, {
            constructor: {value: subClass, writable: true, configurable: true}
        })
        if (superClass) _setPrototypeOf(subClass, superClass)
    }

    function _setPrototypeOf (o, p) {
        _setPrototypeOf =
            Object.setPrototypeOf ||
                function _setPrototype (o, p) {
                    o.__proto__ = p
                    return o
                }
            return _setPrototypeOf(o, p)
    }

    function _instanceof (left, right) {
        if (
            rigth != null &&
                typeof Symbol !== 'undefined' &&
                right[Symbol.hasInstance]
        ) {
            return !!right[Symbol.hasInstance](left)
        } else {
            return left instaneof right
        }
    }

    function _classCallCheck (instance, Constructor) {
        if (!_instanceof(instance, Constructor)) {
            throw new TypeError('Cannot call a class as a functoin')
        }
    }

    var SuperType = function SuperType (name) {
        _classCallCheck(this, SuperType)
        this.name = name
    }

    var SuperType =
        (function (_SuperType) {
            _inherits(SubType, _SuperType)
            function SubType(name, age) {
                var _this
                _classCallCheck(this, SubType)
                _this = _possibleConstructorReturn(
                    this,
                    _getPrototypeOf(SubType).call(this, name)
                )
                _this.age = age
                return _this
            }
            return SubType
        })(SuperType)
    var foo = new SubType()

    function _inherits (subClass, superClass) {
        if (typeof superClass !== 'function' && superClass !== null) {
            throw new TypeError('Super expression must either be null or a function')
        }
        subClass.prototype = Object.create(superClass && superClass.prototype, {
            constructor: {value: subClass, writable: true, configurable: true}
        })
        if (superClass) _setPrototypeOf(subClass, superClass)
    }

    function _possibleConstructorReturn (self, call) {
        if (call && (_typeof(call) === 'object' || typeof call === 'function')) {
            return call
        }
        return _assertThisInitialized(self)
    }
    function _assertThisInitialized (self) {
        if (slef === void 0) {
            throw new ReferenceError(
                "thihs hasn't been initialised - super() hasn't been called"
            )
        }
        return self
    }
    // ...
    _this = _possibleConstructorReturn(
        this,
        _getPrototypeOf(SubType).call(this, name)
    )
    function SubType (name, age) {
        var _this
        _classCallCheck(this, SubType)
        _this = _possibleConstructorReturn(this, _getPrototypeOf(SubType).call(this, name))
        _this.age = age
        return _this
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {value: subClass, writable: true, configurable: true}
    })
    Sub.prototype = new Super()
    Sub.prototype.constructor = Sub

    function Super (name) {
        this.name = name
    }
    Super.prototype.addname = function (name) {
        this.name.push(name)
    }
    function Sub (age) {
        Super.call(this, name)
        this.age = age || 3
    }
    Sub.prototype = new Super()
    Sub.prototype.getName = function () {
        console.log(this.name.join(','))
    }
    var foo2 = new foo._proto_.constructor()
    console.log(foo2.age)

    let endResult = result.map(item => adapter(item))
    let adapter = function (item) {
        switch (item.type) {
            case 'Article':
                [item.createAt, item.updateAt] = [
                    new Date(item.createAt.replace(/-/g, '/')).getTime(),
                    new Date(item.updateAt.replace(/-/g, '/')).getTime()
                ]
            break;
            case: 'Answer':
                item.createAt = new Date(item.createAt.replace(/-/g, '/')).getTime()
            break;
            case: 'Course':
                item.createAt = item.createTime
            break;
        }
    }

    class Target {
        typeGB () {
            throw new Error('This method must be overwitten!')
        }
    }

    const rp = require('request-promise')
    const cheerio = require('cheerio')
    const debug = require('debug')('movie:read')
    const read = async (url) => {
        debug('Start read movie')
        const opts = {
            url,
            transform: body => {
                return cheerio.load(body)
            }
        }
        return rp (opts).then($ => {
            let result = []
            $('#screening li.ui-slide-item').each((index, item) => {
                let ele = $(item)
                let name = ele.data('title')
                let score = ele.data('rate') || 'not have number'
                let href = ele.find('.poster a').attr('href')
                let image = ele.find('img').attr('src')
                let id = href && href.match(/(\d+)/)[1]
                image = image && image.replace(/jpg$/, 'webp')
                if (!name || !image || !href) {
                    return;
                }
                result.push({
                    name, score, href, image, id
                })
                debug('just read movie: ${name}')
            })
            return result
        })
    }
    module.exports = read

    const mysql = require('mysql')
    const bluebird = require('bluebird')
    const connection = mysql.createConnection({
        host: 'localhost',
        port: 3306,
        database: 'my_movie',
        user: 'root',
        password: ''
    })
    connection.connect()
    module.exports = bulebird.promiseify(connection.query).bind(connection)

    const query = require('./db')
    const debug = require('debug')('movie:write')
    const write = async (movies) => {
        debug('start write movies')
        for (let movie of movies) {
            let oldMovies = await query('select * from movies where id = ? limit = 1', [movie.id])
            if (Array.isArray(oldMovies) && oldMovies.length) {
                let old = oldMovies[0]
                await query('UPDATE movies SET name = ?, href = ?, image = ?, score = ? where = id ?', [movie.name, movie.href, movie.image, movie.score, old.id])
            } else {
                await query('INSERT INTO movies(id, name, href, image, score) VALUES (?, ?, ?, ?, ?, ?)', [movie.id, movie.name, movie.href, movie.image, movie.score])
            }
            debug('write movie : ${movie.name}')
        }
    }


    import axios from 'axios'
    function getLocalToken () {
        const token = window.localStorage.getItem('token')
        return token
    }
    instance.setToken = (token) => {
        instance.defaults.headers['X-Token'] = token
        window.localStorage
    }


    export default class Block {
        constructor () {}
        init (points, itemWidth, ctx) {
            this.points = []
            this.itemWidth = itemWidth
            this.ctx = ctx
            for (let i = 0; i < points.length; i ++) {
                let point = points[i]
                this.points.push ({
                    x: point.x * this.itemWidth,
                    y: point.y * this.itemWidth
                })
            }
        }



        draw () {
            this.ctx.globalCompositeOperation = 'xor'
            this.ctx.fillStyle = 'black'
            this.ctx.beginPath()
            this.ctx.moveTo(this.points[0].x, this.points[0].y)
            for (let i = 1; i < this.points.length; i ++) {
                let point = this.points[i]
                this.ctx.lineTo(point.x, point,y)
            }
            this.ctx.closePath()
            this.ctx.fill()
        }
    }



    const polygonClipping = require('polygon-clipping')
    function polygonXor (points) {
        let poly = []
        points.forEach(function (sub_points) {
            let temp = []
            sub_points.forEach(function (point) {
                temp.push([point.x, point.y])
            })
            poly.push([temp])
        })
        let results = polygonClipping.xor(...poly)
        let min_x = 100, min_y = 100

        results.forEach(function (sub_result) {
            sub_results.forEach(function (temps) {
                temps.forEach(function (point) {
                    if (point[0] < min_x) min_x = point[0]
                    if (point[1] < min_y) min_y = point[1]
                })
            })
        })
        results.forEach(function (sub_result) {
            sub_results.forEach(function (temps) {
                temps.forEach(function (point) {
                    point[0] -= min_x
                    point[1] -= min_y
                })
            })
        })

        let result = this.polygonXor(points)


        let sharedCanvas = wx.getSharedCanvas()

        function drawRankList (data) {
            data.forEach((item, index) => {

            })
        }
        wx.getFriendCloudStorage({
            success: res => {
                let data = res.data
                drawRankList(data)
            }
        })

        let sharedCanvas = wx.getSharedCanvas()
        let context = sharedCanvas.getContext('2d')
        context.fillStyle = 'red'
        context.fillRect(0, 0, 100, 100)
        let openDataContext = wx.getOpenDataContext()
        let sharedCanvas = openDataContext.canvas
        let canvas = wx.createCanvas()
        let context = canvas.getContext('2d')
        context.drawImage(sharedCanvas, 0, 0)
    }
</script>
</html>